// Copyright (C) Calum Grant 2008

#include "cppscript"
#include <sstream>

/**	\mainpage

	\section Overview
	C++Script is a library for dynamic typing in C++.

	The implementation is based on the "pimpl idiom", whereby \ref dynamic::var contains a pointer
	to an object of type \ref dynamic::var_impl.  \ref dynamic::var_impl is pure virtual base class
	which is inherited by a number of implementation classes, such as \ref dynamic::types::null_impl, 
	\ref dynamic::types::int_impl etc.

	Operator overloading is used to allow \ref dynamic::var to be used with a natural syntax, 
	but this is really just sugar around the implementation.  \ref dynamic::internal::var_methods 
	implements most of the methods on the \ref dynamic::var,
	simply because there are other "var-like" classes (such as \ref dynamic::internal::var_member).

	\ref dynamic::var wraps \ref dynamic::var_variant, which is a 16-byte buffer containing an object 
	of type \ref dynamic::var_impl.  If the object is small then it is stored within the var itself, 
	but if the object is large, then the var contains a pointer to a \ref dynamic::shared_var_impl.  
	This essentially explains the behaviour of integers which are copied by value, but larger 
	objects such as strings are always copied by reference.

	\section org Code Organization

	The code is organised into the following modules:

	1) \ref syntax are used to implement operators, and allow the seamless use of
	\ref var by the user.  The type visible to the user is \ref dynamic::var, however there are
	various supporting classes and functions.

	2) \ref impl implement the various types.  
	All implementation classes inherit from the pure virtual base class \ref dynamic::var_impl.

	3) \ref lib.  Many of these are functions which create a \ref dynamic::var containing
	one of the \ref impl classes.

	4) \ref mem includes garbage collection and threading, which provide the
	underlying support for the implementation.

	5) \ref test are the unit testing functions.

	6) \ref exceptions
*/

/** \defgroup syntax Syntax classes

	These classes provide a "natural" syntax for the programmer.
	This is achieved through operator overloading, and a number of helper classes
	for some operators like extend(), operator[] and operator->.

	\defgroup impl Types

	These classes implement the different type of object stored by a var.

	\defgroup lib Library functions and utilities

	\defgroup mem Memory and thread management

	These functions and classes implement the memory and thread infrastructure.

	\defgroup test Unit tests

	\defgroup exceptions Exceptions and error-handling
*/

not_supported::not_supported(const var & v, std::string const & op) : 
	exception("not_supported", "Operation '" + op + "' not supported on " + v.as_string())
{
}


not_supported::~not_supported() throw ()
{
}


static std::string build_error(var_impl * v, std::string const & op)
{
	std::ostringstream ss;
	ss << "Operation '" << op << "' not supported on ";
	v->output(ss);
	return ss.str();
}


not_supported::not_supported(var_impl * v, std::string const & op) : 
	exception("not_supported", build_error(v,op) )
{
}


not_found::not_found(var const & e) :
	exception("not_found", "No such member: " + e.as_string(), e)
{
}


not_found::~not_found() throw()
{ 
}


exception::exception() : 
	std::runtime_error("exception"),
	var( object("exception").extend("text", "exception")("data", var())("stack", stack_trace()) )
{
}


exception::exception(const char * class_name) : 
	std::runtime_error(class_name),
	var( object(class_name).extend("text", class_name)("data", var())("stack", stack_trace()) )
{
}


exception::exception(const char * class_name, const var & message) : 
	std::runtime_error(message.as_string().c_str()),
	var( object(class_name).extend("text", message)("data", var())("stack", stack_trace()) )
{
}


exception::exception(const char * class_name, const var & message, const var & data) : 
	std::runtime_error(message.as_string().c_str()),
	var( object(class_name).extend("text", message)("data", data)("stack", stack_trace()) )
{
}


dynamic::exception::~exception() throw() 
{ 
}


namespace
{
	std::string build_args_string(int expected, int actual)
	{
		std::ostringstream ss;
		ss << "Wrong number of arguments: expected " << expected << " arguments but received " << actual << " arguments";
		return ss.str();
	}
}


wrong_number_of_args::wrong_number_of_args(int e, int a) : 
	exception("wrong_number_of_args", build_args_string(e,a))
{
}


wrong_number_of_args::~wrong_number_of_args() throw() 
{ 
}


invalid_string::invalid_string() : exception("invalid_string", "Invalid string")
{
}


invalid_string::~invalid_string() throw()
{
}


too_many_arguments::too_many_arguments() : exception("too_many_arguments", "Too many arguments")
{
}


too_many_arguments::~too_many_arguments() throw()
{
}


expired_iterator::expired_iterator() : exception("expired_iterator", "Expired iterator")
{
}


expired_iterator::~expired_iterator() throw()
{
}


std::ostream & dynamic::operator<<(std::ostream & os, var const & a)
{
	a.impl().output(os);
	return os;
}


std::basic_ostream<wchar_t> & dynamic::operator<<(std::basic_ostream<wchar_t> & os, var const & a)
{
	a.impl().output(os);
	return os;
}


var dynamic::global()
{
	static var global_var(global(object));
	return global_var;
}


var dynamic::exception_description(const var & ex)
{
	// Handle other types of exception as well.
	if( !ex.contains("text") || !ex.contains("stack") )
		return ex;

	std::ostringstream ss;

	ss << var(ex["text"]) << " (" << ex.class_name() << ")";

	bool first=false;

	foreach( i, ex["stack"] )
	{
		if(first)
		{
			first=false;
			ss << "\n    in function " << i;
		}
		else
			ss << "\n    called from " << i;
	}

	return ss.str();
}


var dynamic::version()
{
	return "0.9.4";  /// \todo Avoid hard-coding this here
}
